<?php
// +----------------------------------------------------------------------
// | OneThink [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://www.onethink.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace Admin\Base;

use DeCMF\Core\Controller\Base as BaseController;
use Admin\Model\AuthRuleModel;

/**
 * 后台控制器基类,主要实现权限校验,后台菜单构建
 * 
 */
class Controller extends BaseController
{

    /**
     * 后台控制器初始化
     */
    protected function _initialize()
    {
        // 获取当前用户ID
        parent::_initialize();
        
        if (!UID) { // 还没登录 跳转到登录页面
            $this->redirect('Public/login');
        }
        /* 读取数据库中的配置 */
        $config = S('DB_CONFIG_DATA');
        if (! $config) {
            $config = api('Config/lists');
            S('DB_CONFIG_DATA', $config);
        }
        C($config); // 添加配置
                    
        // 是否是超级管理员
        define('IS_ROOT', is_administrator());
        if (! IS_ROOT && C('ADMIN_ALLOW_IP')) {
            // 检查IP地址访问
            if (! in_array(get_client_ip(), explode(',', C('ADMIN_ALLOW_IP')))) {
                $this->error('403:禁止访问');
            }
        }
        // 检测系统权限
        if (! IS_ROOT) {
            $access = $this->accessControl();
            if (false === $access) {
                $this->error('403:禁止访问');
            } elseif (null === $access) {
                // 检测访问权限
                $rule = strtolower(MODULE_NAME . '/' . CONTROLLER_NAME . '/' . ACTION_NAME);
                if (! $this->checkRule($rule, array(
                    'in',
                    '1,2'
                ))) {
                    $this->error('未授权访问!');
                } else {
                    // 检测分类及内容有关的各项动态权限
                    $dynamic = $this->checkDynamic();
                    if (false === $dynamic) {
                        $this->error('未授权访问!');
                    }
                }
            }
        }
        
        $this->assign('__MENU__', $this->getMenus());
    }

    /**
     * 权限检测
     * 
     * @param string $rule
     *            检测的规则
     * @param string $mode
     *            check模式
     * @return boolean
     * @author 朱亚杰 <xcoolcc@gmail.com>
     */
    final protected function checkRule($rule, $type = AuthRuleModel::RULE_URL, $mode = 'url')
    {
        static $Auth = null;
        if (! $Auth) {
            $Auth = new \Think\Auth();
        }
        if (! $Auth->check($rule, UID, $type, $mode)) {
            return false;
        }
        return true;
    }

    /**
     * 检测是否是需要动态判断的权限
     * 
     * @return boolean|null 返回true则表示当前访问有权限
     *         返回false则表示当前访问无权限
     *         返回null，则表示权限不明
     *        
     * @author 朱亚杰 <xcoolcc@gmail.com>
     */
    protected function checkDynamic()
    {}

    /**
     * action访问控制,在 **登陆成功** 后执行的第一项权限检测任务
     *
     * @return boolean|null 返回值必须使用 `===` 进行判断
     *        
     *         返回 **false**, 不允许任何人访问(超管除外)
     *         返回 **true**, 允许任何管理员访问,无需执行节点权限检测
     *         返回 **null**, 需要继续执行节点权限检测决定是否允许访问
     * @author 朱亚杰 <xcoolcc@gmail.com>
     */
    final protected function accessControl()
    {
        $allow = C('ALLOW_VISIT');
        $deny = C('DENY_VISIT');
        $check = strtolower(CONTROLLER_NAME . '/' . ACTION_NAME);
        if (! empty($deny) && in_array_case($check, $deny)) {
            return false; // 非超管禁止访问deny中的方法
        }
        if (! empty($allow) && in_array_case($check, $allow)) {
            return true;
        }
        return null; // 需要检测节点权限
    }

    /**
     * 获取控制器菜单数组,二级菜单元素位于一级菜单的'_child'元素中
     * 
     * @author 朱亚杰 <xcoolcc@gmail.com>
     */
    final public function getMenus($controller = CONTROLLER_NAME)
    {
        $menus = session('ADMIN_MENU_LIST.' . $controller);
        if (empty($menus)) {
            // 获取主菜单
            $where['pid'] = 0;
            $where['hide'] = 0;
            if (! C('DEVELOP_MODE')) { // 是否开发者模式
                $where['is_dev'] = 0;
            }
            $menus['main'] = M('Menu')->where($where)
                ->order('sort asc')
                ->field('id,title,url')
                ->select();
            $menus['child'] = array(); // 设置子节点
            foreach ($menus['main'] as $key => $item) {
                // 判断主菜单权限
                if (! IS_ROOT && ! $this->checkRule(strtolower(MODULE_NAME . '/' . $item['url']), AuthRuleModel::RULE_MAIN, null)) {
                    unset($menus['main'][$key]);
                    continue; // 继续循环
                }
                if (strtolower(CONTROLLER_NAME . '/' . ACTION_NAME) == strtolower($item['url'])) {
                    $menus['main'][$key]['class'] = 'current';
                }
            }
            
            // 查找当前子菜单
            $pid = M('Menu')->where("pid !=0 AND url like '%{$controller}/" . ACTION_NAME . "%'")->getField('pid');
            if ($pid) {
                // 查找当前主菜单
                $nav = M('Menu')->find($pid);
                if ($nav['pid']) {
                    $nav = M('Menu')->find($nav['pid']);
                }
                foreach ($menus['main'] as $key => $item) {
                    // 获取当前主菜单的子菜单项
                    if ($item['id'] == $nav['id']) {
                        $menus['main'][$key]['class'] = 'current';
                        // 生成child树
                        $groups = M('Menu')->where(array(
                            'group' => array(
                                'neq',
                                ''
                            ),
                            'pid' => $item['id']
                        ))
                            ->distinct(true)
                            ->getField("group", true);
                        // 获取二级分类的合法url
                        $where = array();
                        $where['pid'] = $item['id'];
                        $where['hide'] = 0;
                        if (! C('DEVELOP_MODE')) { // 是否开发者模式
                            $where['is_dev'] = 0;
                        }
                        $second_urls = M('Menu')->where($where)->getField('id,url');
                        
                        if (! IS_ROOT) {
                            // 检测菜单权限
                            $to_check_urls = array();
                            foreach ($second_urls as $key => $to_check_url) {
                                if (stripos($to_check_url, MODULE_NAME) !== 0) {
                                    $rule = MODULE_NAME . '/' . $to_check_url;
                                } else {
                                    $rule = $to_check_url;
                                }
                                if ($this->checkRule($rule, AuthRuleModel::RULE_URL, null))
                                    $to_check_urls[] = $to_check_url;
                            }
                        }
                        // 按照分组生成子菜单树
                        foreach ($groups as $g) {
                            $map = array(
                                'group' => $g
                            );
                            if (isset($to_check_urls)) {
                                if (empty($to_check_urls)) {
                                    // 没有任何权限
                                    continue;
                                } else {
                                    $map['url'] = array(
                                        'in',
                                        $to_check_urls
                                    );
                                }
                            }
                            $map['pid'] = $item['id'];
                            $map['hide'] = 0;
                            if (! C('DEVELOP_MODE')) { // 是否开发者模式
                                $map['is_dev'] = 0;
                            }
                            $menuList = M('Menu')->where($map)
                                ->field('id,pid,title,url,tip')
                                ->order('sort asc')
                                ->select();
                            $menus['child'][$g] = list_to_tree($menuList, 'id', 'pid', 'operater', $item['id']);
                        }
                    }
                }
            }
            session('ADMIN_MENU_LIST.' . $controller, $menus);
        }
        return $menus;
    }

    /**
     * 返回后台节点数据
     * 
     * @param boolean $tree
     *            是否返回多维数组结构(生成菜单时用到),为false返回一维数组(生成权限节点时用到)
     *            @retrun array
     *            
     *            注意,返回的主菜单节点数组中有'controller'元素,以供区分子节点和主节点
     *            
     * @author 朱亚杰 <xcoolcc@gmail.com>
     */
    final protected function returnNodes($tree = true)
    {
        static $tree_nodes = array();
        if ($tree && ! empty($tree_nodes[(int) $tree])) {
            return $tree_nodes[$tree];
        }
        if ((int) $tree) {
            $list = M('Menu')->field('id,pid,title,url,tip,hide')
                ->order('sort asc')
                ->select();
            foreach ($list as $key => $value) {
                if (stripos($value['url'], MODULE_NAME) !== 0) {
                    $list[$key]['url'] = MODULE_NAME . '/' . $value['url'];
                }
            }
            $nodes = list_to_tree($list, $pk = 'id', $pid = 'pid', $child = 'operator', $root = 0);
            foreach ($nodes as $key => $value) {
                if (! empty($value['operator'])) {
                    $nodes[$key]['child'] = $value['operator'];
                    unset($nodes[$key]['operator']);
                }
            }
        } else {
            $nodes = M('Menu')->field('title,url,tip,pid')
                ->order('sort asc')
                ->select();
            foreach ($nodes as $key => $value) {
                if (stripos($value['url'], MODULE_NAME) !== 0) {
                    $nodes[$key]['url'] = MODULE_NAME . '/' . $value['url'];
                }
            }
        }
        $tree_nodes[(int) $tree] = $nodes;
        return $nodes;
    }

}
